package com.free4inno.knowledgems.utils;


import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;

/**
 * 多线程的下载器 javase代码
 *
 */
public class MultiDownloader {
    /**
     * 线程的数量
     */
    private static int threadCount = 3;

    /**
     * 正在运行的线程的个数
     */
    private static int runningThreadCount ;

    public static void main(String[] args) throws Exception {
        //采用多个线程把服务器的资源下载下来。
        String path = "http://filem.free4inno.com/project/downloadFile?id=879";//推荐使用exe文件
        URL url  = new URL(path);
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        conn.setRequestMethod("GET");
        conn.setConnectTimeout(5000);
        int code = conn.getResponseCode();
        if(code == 200){
            int length = conn.getContentLength();
            System.out.println("服务器文件的大小为："+length);
            //1.在本地创建一个大小和服务器一模一样的空文件。RandomAccessFile
//            RandomAccessFile raf = new RandomAccessFile(getFileName(path), "rw");
            RandomAccessFile raf = new RandomAccessFile("52.mp4", "rw");
            raf.setLength(length);
            raf.close();
            conn.disconnect();  // 断开http
            //2.等份服务器的资源为若干份，让每个子线程下载自己的部分。
            int blocksize = length/threadCount;
            runningThreadCount = threadCount;
            for(int threadid =0;threadid<threadCount;threadid++){
                int startIndex = threadid*blocksize;
                int endIndex = (threadid+1)*(blocksize)-1;
                //特殊情况
                if(threadid==(threadCount-1)){
                    //最后一个线程。结束位置为文件总长度-1
                    endIndex = length -1;
                }
                new DownloadThread(threadid, startIndex, endIndex, path).start();
            }
        }
    }
    /**
     * 下载资源的线程
     *
     */
    public static class DownloadThread extends Thread{
        private int threadid;
        private int startindex;
        private int endindex;
        private String path;
        public DownloadThread(int threadid, int startindex, int endindex,
                              String path) {
            this.threadid = threadid;
            this.startindex = startindex;
            this.endindex = endindex;
            this.path = path;
        }
        @Override
        public void run() {
            System.out.println("线程id:"+threadid+"理论下载位置："+startindex+"~"+endindex);
            try {
                URL url = new URL(path);
                HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                conn.setRequestMethod("GET");
                conn.setConnectTimeout(5000);
                //查看文件里面是否记录有当前线程的下载开始位置。
                File infofile = new File(threadCount+getFileName(path)+threadid+".txt");
                if(infofile.exists()&&infofile.length()>0){
                    FileInputStream fis = new FileInputStream(infofile);
                    BufferedReader br = new BufferedReader(new InputStreamReader(fis));
                    String newstartindex = br.readLine();
                    conn.setRequestProperty("Range", "bytes="+newstartindex+"-"+endindex);
                    //	System.out.println("线程id："+threadid+"真实的下载位置："+newstartindex+"~"+endindex);
                    startindex = Integer.parseInt(newstartindex);
                    fis.close();//记得释放文件的引用
                }else{
                    //不是下载整个文件，而是下载文件的一部分。 告诉服务器 下载的资源就是一部分
                    //HttpURLConnection.setRequestProperty("Range", "bytes=2097152-4194303");
                    conn.setRequestProperty("Range", "bytes="+startindex+"-"+endindex);
                    System.out.println("线程id："+threadid+"真实的下载位置："+startindex+"~"+endindex);
                }
                int code = conn.getResponseCode();//200 OK  206 请求部分数据成功
                System.out.println("返回状态码：code"+ code );
                if(code == 206){
                    //当前的线程 就应用下载这一部分的数据。
                    InputStream is = conn.getInputStream();
//                    RandomAccessFile raf = new RandomAccessFile(getFileName(path), "rw");
                    RandomAccessFile raf = new RandomAccessFile("52.mp4", "rw");
                    raf.seek(startindex);//注意：不同线程存文件的开始位置是不相同的，要从自己对应的位置存文件
                    byte[] buffer = new byte[1024*4];
                    int len = -1;
                    int total = 0;//代表当前线程下载的总大小
                    while(( len = is.read(buffer))!=-1){
                        raf.write(buffer, 0, len);
                        total+=len;
                        int currentposition = startindex+total;//当前线程下载的位置。
                        System.err.println("线程id："+threadid+  " 下载到:"+ currentposition);
                        File file = new File(threadCount+getFileName(path)+threadid+".txt");
                        //确保了每次循环都会把进度写到底层存储设备里面。
                        /**  r: 文件只读方式打开
                         *   rw: 文件读写方式打开
                         * rws: 文件读写方式打开,对文件内容或者元数据的每个更新都同步到写入底层设备
                         rwd:文件读写方式打开, 对文件内容的每个更新都写入到底层设备，断电了数据也立刻写入
                         **/
//                        RandomAccessFile rafos = new RandomAccessFile(file, "rwd");
                        RandomAccessFile rafos = new RandomAccessFile("52.mp4", "rw");
                        //把当前线程的位置信息写入到一个文件里面
                        rafos.write(String.valueOf(currentposition).getBytes());
                        rafos.close();//数据并不是直接保存到底层的存储设备里面，保存到缓存，缓存空间满了，数据会同步到底层设备。
                    }
                    is.close();
                    raf.close();
                    System.out.println("线程："+threadid+"下载完毕了。");
                    synchronized (MultiDownloader.class) {
                        runningThreadCount--;
                        if(runningThreadCount<=0){
                            System.out.println("线程全部下载完毕了。");
                            for(int i=0;i<threadCount;i++){
                                File f = new File(threadCount+getFileName(path)+i+".txt");
                                f.delete();
                            }
                        }
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    /**
     * 获取文件名称
     * @param path
     * @return
     */
    public static String getFileName(String path){
        int startindex = path.lastIndexOf("/")+1;
        return path.substring(startindex);
    }
}